﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace WordpressRestApi
{
    /// <summary>Base class containing common methods for invoking APIs.</summary>
    public abstract class ApiClientBase : TokenOwnerBase
    {
        /// <summary>API ROOT URL.</summary>
        protected static readonly string API_ROOT = "https://public-api.wordpress.com/rest/v1.1/";

        /// <summary>Constructor.</summary>
        /// <param name="token">Token owned by this component.</param>
        public ApiClientBase(TokenInfo token)
            : base(token)
        {
        }

        /// <summary>Invokes an HTTP-GET with token and returns the returned payload.</summary>
        /// <param name="url">Target URL.</param>
        /// <returns>Payload returned.</returns>
        protected async Task<string> GetRequestAsync(string url)
        {
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token.AccessToken);

                using (var response = await client.GetAsync(url))
                {
                    var json = await response.Content.ReadAsStringAsync();

                    return json;
                }
            }
        }

        /// <summary>Strong type HTTP-GET:  invokes HTTP-GET with token and deserialize the returned payload (JSON).</summary>
        /// <typeparam name="T">Type of object to deserialize the payload into.</typeparam>
        /// <param name="url">Target URL.</param>
        /// <returns>Deserialized payload.</returns>
        protected async Task<T> GetRequestAsync<T>(string url)
        {
            var json = await GetRequestAsync(url);
            var obj = JsonConvert.DeserializeObject<T>(json);

            return obj;
        }

        /// <summary>Invokes an HTTP-POST with token and returns the returned payload.</summary>
        /// <param name="url">Target URL.</param>
        /// <param name="parameters">Post parameters, key-value pairs.</param>
        /// <returns>Payload returned.</returns>
        protected async Task<string> PostRequestAsync(string url, params Tuple<string, string>[] parameters)
        {
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token.AccessToken);

                using (var stream = new MemoryStream())
                using (var writer = new StreamWriter(stream))
                {
                    var equalParams = from p in parameters
                                      select p.Item1 + "=" + p.Item2;
                    var inputPayload = string.Join("&", equalParams);

                    writer.Write(inputPayload);
                    writer.Flush();
                    stream.Flush();
                    stream.Position = 0;

                    using (var content = new StreamContent(stream))
                    using (var response = await client.PostAsync(url, content))
                    {
                        var outputPayload = await response.Content.ReadAsStringAsync();

                        if (!response.IsSuccessStatusCode)
                        {
                            throw new InvalidOperationException(
                                $"{response.StatusCode} - {(int)response.StatusCode}"
                                + Environment.NewLine
                                + outputPayload);
                        }
                        else
                        {
                            return outputPayload;
                        }
                    }
                }
            }
        }

        /// <summary>Strong type HTTP-POST:  invokes HTTP-POST with token and deserialize the returned payload (JSON).</summary>
        /// <typeparam name="T">Type of object to deserialize the payload into.</typeparam>
        /// <param name="url">Target URL.</param>
        /// <param name="parameters">Post parameters, key-value pairs.</param>
        /// <returns>Deserialized payload.</returns>
        protected async Task<T> PostRequestAsync<T>(string url, params Tuple<string, string>[] parameters)
        {
            var json = await PostRequestAsync(url, parameters);
            var obj = JsonConvert.DeserializeObject<T>(json);

            return obj;
        }
    }
}